package org.simpleframework.inject;

import org.simpleframework.core.BeanContainer;
import org.simpleframework.inject.annotation.Autowired;
import org.simpleframework.util.ClassUtil;
import org.simpleframework.util.ValidationUtil;

import java.lang.reflect.Field;
import java.util.Set;

/**
 * 提供依赖注入服务
 * 依赖注入，针对被容器管理的Class对象，所以需要持有容器实例
 */
public class DependencyInject {
    // 容器实例
    private BeanContainer beanContainer;

    /**
     * 为了在依赖注入服务对象创建时，就能获取到容器的实例
     * 所以在构造函数中，构造容器的实例
     */
    public DependencyInject(){
        beanContainer = BeanContainer.getInstance();
    }

    // 对整个容器里面的Class对象，去做依赖注入处理
    public void doIoc(){
        if(ValidationUtil.isEmpty(beanContainer.getClasses())){
            return;
        }
        //1.遍历容器中的所有的Class对象
        for(Class clazz:beanContainer.getClasses()){
            //2.遍历Class对象的所有成员变量
            Field fields[] = clazz.getDeclaredFields();
            if(fields.length <=0){
                continue;
            }
            for(Field field:fields){
                //3.找出被Autowired标记的成员变量
                if(field.isAnnotationPresent(Autowired.class)){
                    Autowired autowired = field.getAnnotation(Autowired.class);
                    String autowiredValue = autowired.value();
                    //4.获取这些成员变量的类型
                    Class<?> fieldClass = field.getType();
                    //5.获取这些成员变量的类型在容器里对应的实例
                    Object fieldValue = getFieldInstance(fieldClass,autowiredValue);
                    if(fieldValue == null){
                        throw new RuntimeException
                                ("无法从容器总获得成员变量的类型"+fieldClass.getName()+autowiredValue);
                    }else {
                        //6.获取成员变量所在的类的实例
                        Object targetBean = beanContainer.getBean(clazz);
                        //7.通过反射给实例的成员变量设值
                        ClassUtil.setField(field,targetBean,fieldValue,true);
                    }
                }
            }
        }
    }

    /**
     * 根据Class对象，在beanContainer里获取其实例
     * 或者接口对应的实现类
     * @param fieldCLass
     * @return
     */
    private Object getFieldInstance(Class<?> fieldCLass,String autowiredValue) {
        // 获取成员变量对应的实例
        Object fieldValue = beanContainer.getBean(fieldCLass);
        // 如果不为空，说明成员变量类型不是接口，而是实现类
        if(fieldValue != null){
            return fieldValue;
        }else{
            // 获取成员变量对应的实现类（成员变量的类型是接口，而接口并未被容器管理，是有实现类被容器管理了）
            Class<?> implementedClass = getImplementClass(fieldCLass,autowiredValue);
            if(implementedClass != null){
                //说明接口对应的实现类在容器中是存在的
                return beanContainer.getBean(implementedClass);
            }else {
                return null;
            }
        }
    }

    /**
     * 通过接口或者父类获取实现类或者子类的Class集合，不包括其本身
     * @param fieldCLass
     * @return
     */
    private Class<?> getImplementClass(Class<?> fieldCLass,String autowiredValue) {
        Set<Class<?>> classSet = beanContainer.getClassesBySuper(fieldCLass);
        if(!ValidationUtil.isEmpty(classSet)){
            //同一个就接口有多个实现类
            if(autowiredValue == null || "".equals(autowiredValue)){
                // 说明只有一个实现类，返回即可
                if(classSet.size() == 1){
                    return classSet.iterator().next();
                }else {
                    //如果多于两个实现类且用户未指定其中一个实现类，则抛出异常
                    throw new RuntimeException("multiple implemented classes for" + fieldCLass.getClass());
                }
            }
            // 说明@Autowired的value设置了具体实现的值
            else {
                for(Class<?> clazz:classSet){
                    if(autowiredValue.equals(clazz.getSimpleName())){
                        return clazz;
                    }
                }
            }
        }
        return null;
    }
}
